﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Net;
// using System.Web.Http.Filters;
using System.Threading;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Authentication;
using Daemon.ViewModel;
using Daemon.Common.Middleware;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Caching.Memory;
using Daemon.Common.Cache;
using Daemon.Common.Const;
using Daemon.Model;
using Daemon.Common;
using DaemonAPI.Helpers;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using Daemon.Repository.Contract;
using Daemon.Data.Infrastructure.Auth;
using Daemon.Common.Auth;

namespace DaemonAPI.Filter
{
	/// <summary>
	/// 
	/// </summary>
	public class DaemonAuthFilter : ActionFilterAttribute
	{
		private const int DEFAULT_SESSION_TIMEOUT = 60 * 24;

		private readonly IConfiguration _configuration;

		private readonly IDaemonDistributedCache _cache;

		private readonly ApiDBContent _context;

		private readonly ITokenInfoRepository _tokenInfoRepository;

		private readonly IClientConfigRepository _clientConfigRepository;

		private readonly IAdminUserRepository _adminUserRepository;

		private readonly ClientAuthCache _clientAuthCache;

		public DaemonAuthFilter(IConfiguration configuration, IDaemonDistributedCache cache, ITokenInfoRepository tokenInfoRepository, IClientConfigRepository clientConfigRepository, IAdminUserRepository adminUserRepository, ClientAuthCache clientAuthCache, ApiDBContent context)
		{
			this._configuration = configuration;
			_cache = cache;
			_context = context;
			_tokenInfoRepository = tokenInfoRepository;
			_clientConfigRepository = clientConfigRepository;
			_clientAuthCache = clientAuthCache;
			_adminUserRepository = adminUserRepository;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="context"></param>
		public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
		{
			if (SkipAuthorization(context))
			{
				await next();
				return;
			}

			var token = context.HttpContext.Request.Headers["token"].FirstOrDefault();
			if (string.IsNullOrEmpty(token))
			{
				token = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault();
			}

			var claims = TokenHelper.ValidateToken(token);
			if (claims != null)
			{
				var userId = claims.FirstOrDefault(r => r.Type == "Id")?.Value;
				var prefix = claims.FirstOrDefault(r => r.Type == "Prefix")?.Value;
				var userName = claims.FirstOrDefault(r => r.Type == "Name")?.Value;
				if (string.IsNullOrEmpty(userId))
				{
					context.Result = new ResultModel(HttpStatusCode.Unauthorized, "");
				}

				IAuthInfo authInfo = _clientAuthCache.GetAuthInfoByClientUser(prefix + "_" + userName);
				BaseAuthUser userEntity = null;
				if (authInfo != null)
				{
					userEntity = authInfo.UserEntity;
				}

				if (userEntity == null)
				{
					try
					{
						var user = _adminUserRepository.FindAll().Where(r => r.Id == Convert.ToInt32(userId)).Select(r => new BaseAuthUser() { Id = r.Id, UserType = "WX", UserName = r.UserName }).ToList()[0];
						authInfo = new AuthInfoEF(user);
						userEntity = authInfo?.UserEntity;
						_clientAuthCache.AddClientUserEntryAsync(prefix + "_" + userName, authInfo);
					}
					catch (Exception ex)
					{
						context.Result = new ResultModel((HttpStatusCode)422, ex.Message);
						return;
					}
				}

				if (userEntity == null)
				{
					context.Result = new ResultModel((HttpStatusCode)422, "Invalid username or password");
					return;
				}
			}

			await next();
		}

		private bool SkipAuthorization(ActionExecutingContext actionContext)
		{
			Contract.Assert(actionContext != null);
			var requestPath = actionContext.HttpContext.Request.Path;
			var header = actionContext.HttpContext.Request.Headers["Auth-key"];
			if (header.Any() && !string.IsNullOrEmpty(header.FirstOrDefault()))
			{
				var authKey = PasswordHelper.Rfc2898Decrypt(header.FirstOrDefault(), "daemon");
				if (authKey == GetAuthKey())
				{
					return true;
				}
			}

			var attributeList = (actionContext.ActionDescriptor as ControllerActionDescriptor).MethodInfo.GetCustomAttributes(true);
			var controllerAttribute = actionContext.Controller.GetType().CustomAttributes;
			var controllerAuthorityAttribute = (Daemon.Common.Attribute.AllowAnonymousAttribute)attributeList.Where(a => a.GetType().Name == typeof(Daemon.Common.Attribute.AllowAnonymousAttribute).Name).ToList().FirstOrDefault();
			if (controllerAuthorityAttribute != null)
			{
				return true;
			}

			return false;
		}

		private string GetAuthKey()
		{
			var authKey = _cache.Get<string>(CacheKeyConst.AUTH_KEY);
			if (!string.IsNullOrEmpty(authKey))
			{
				return authKey;
			}

			authKey = _context.ClientConfig.FirstOrDefault()?.AuthKey;
			return authKey;
		}


		public class BE_PostPermissionList
		{
			public string permid { get; set; }

			public int ModuleId { get; set; }
		}



	}

}
